package in

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"regexp"
	"sort"
	"strings"

	"github.com/gen2brain/beeep"
	"github.com/gen2brain/dlgs"
)

const (
	SEP      = "<|SEP|>"
	FILE_TAG = "<|FILE|>"
	INPUT    = 0
	PASS     = 1
	LIST     = 2
	FILE     = 3
	FILES    = 4
	DIR      = 5
	MSG      = 6
)

type Flow struct {
	Tp      uint16
	Title   string
	Info    string
	Default string
}

func Exists(filePath string) bool {
	_, err := os.Stat(filePath)
	if err != nil {
		return false
	}

	return true
}

func IsDir(filePath string) bool {
	f, err := os.Stat(filePath)
	if err != nil {
		return false
	}
	return f.IsDir()
}

func (flow *Flow) Action() (use bool, output string, err error) {
	switch flow.Tp {
	case INPUT:
		output, use, err = dlgs.Entry(flow.Title, flow.Info, flow.Default)

	case PASS:
		output, use, err = dlgs.Password(flow.Title, flow.Info)
	case FILE:
		output, use, err = dlgs.File(flow.Title, flow.Default, false)
	case FILES:

		if outputs, use, err := dlgs.FileMulti(flow.Title, flow.Default); use && err == nil {
			output = strings.Join(outputs, SEP)
			use = true
		}
	case LIST:
		output, use, err = dlgs.List(flow.Title, flow.Info, strings.Split(flow.Default, SEP))
	case DIR:
		output, use, err = dlgs.File(flow.Title, flow.Default, true)
	default:
		err = beeep.Notify(flow.Title, flow.Info, "information.png")
	}
	return
}

/*
FlowFrom :
 1. entry:
	> some thing : xxxx
 2. Password:
	> some thing *: xxx
 3. List:
	 some sdsd<|SEP|> afasfs<|SEP|> asfsf<|SEP|>
 4. File:
	 <|FILE|>
 5. MSG:
	other ....

*/
func FlowFrom(content string) *Flow {

	if strings.Contains(content, ":") && strings.HasPrefix(content, ">") {
		S := strings.SplitN(content, ":", 2)
		flow := &Flow{
			Tp:      INPUT,
			Title:   "Entry input",
			Info:    S[0] + ":",
			Default: S[1],
		}
		if strings.HasSuffix(strings.TrimSpace(S[0]), "*") {
			flow.Tp = PASS
		}
		return flow
	} else if strings.Contains(content, SEP) {
		return &Flow{
			Tp:      LIST,
			Title:   "Menu",
			Info:    "List Choose:",
			Default: content,
		}
	} else {
		if content == FILE_TAG {
			return &Flow{
				Tp:      FILES,
				Title:   "Choose some files",
				Info:    "Choose files:",
				Default: content,
			}
		} else {
			return &Flow{
				Tp:    MSG,
				Title: "Information",
				Info:  content,
			}
		}

	}
	return nil
}

func FlowFromList(contents ...string) *Flow {
	sort.Slice(contents, func(i, j int) bool {
		ll := len(contents[i])
		rl := len(contents[j])
		if ll == rl {
			return byte(contents[i][0]) < byte(contents[j][0])
		} else {
			return ll < rl
		}
	})
	if len(contents) > 1 {
		return &Flow{
			Tp:      LIST,
			Title:   "Menu",
			Info:    "List Choose:",
			Default: strings.Join(contents, SEP),
		}
	} else if len(contents) == 1 {
		content := contents[0]
		if strings.Contains(content, ":") && strings.HasPrefix(content, ">") {
			S := strings.SplitN(content, ":", 2)
			flow := &Flow{
				Tp:      INPUT,
				Title:   "Entry input",
				Info:    S[0] + ":",
				Default: S[1],
			}
			if strings.HasSuffix(strings.TrimSpace(S[0]), "*") {
				flow.Tp = PASS
			}
			return flow
		} else {
			if content == FILE_TAG {
				return &Flow{
					Tp:      FILES,
					Title:   "Choose some files",
					Info:    "Choose files:",
					Default: content,
				}
			} else {
				return &Flow{
					Tp:    MSG,
					Title: "Information",
					Info:  content,
				}
			}

		}
	}
	return nil
}

func (flow *Flow) ActionWith(do func(tp uint16, output string, err error) string) {

	use, output, err := flow.Action()
	if err != nil || use == false || output == "" {
		log.Println("Err in:", flow.Title)
		return
	}
	s := do(flow.Tp, output, err)
	for {
		nextFlow := FlowFrom(s)
		use, output, err = nextFlow.Action()
		if err != nil || use == false || output == "" {
			log.Println("Err in:", flow.Title)
			return
		}
		s = do(flow.Tp, output, err)
	}

	return
}

func Test() {
	f := FlowFrom("Notify some thing .....")
	if f != nil {
		output, use, err := f.Action()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(use, output)
	}

}

func SearchByUI(after func(bottle BottleInterface, oper *Oper), errHandle func(error)) {
	_, new_data_source, _ := FlowFromList(">数据源 :").Action()

	bottle, err := AutoBottle(ParsePath(new_data_source))
	if err != nil {
		errHandle(err)
		return
	}
	var oper *Oper
	btp := bottle.GetTp()
	ColorL("Bottle Type:", btp)
	if btp == "mongodb" {
		if use, db, err := FlowFromList("> 输入数据库表名/Collection 名字:").Action(); use && err == nil {

			oper = OperWith(db)
		} else {
			errHandle(err)
			return
		}
	} else if strings.Contains(bottle.GetTp(), "sql") {
		tbs := bottle.Desc(OperWith(""))
		if len(tbs) == 1 {
			oper = OperWith(tbs[0])
		} else {
			if use, tb, err := FlowFromList(tbs...).Action(); use && err == nil {
				oper = OperWith(tb)
			} else {
				errHandle(err)
			}
		}

	} else if bottle.GetTp() == "SqlFile" {
		tbs := bottle.Desc(OperWith(""))
		sort.Slice(tbs, func(i, j int) bool {
			if len(tbs[i]) == len(tbs[j]) {
				return byte(tbs[i][0]) < byte(tbs[j][0])
			} else {
				return len(tbs[i]) < len(tbs[j])
			}
		})
		if len(tbs) > 1 {
			if use, tb, err := FlowFromList(tbs...).Action(); use && err == nil {
				oper = OperWith(tb)
			} else {
				errHandle(err)
				log.Println("[Ui create Error:]", err)
			}

		} else {
			oper = OperWith(tbs[0])
		}

	} else {
		oper = OperWith("")
	}
	var filterData G
	if use, filterStr, err := FlowFromList("> 如何查找 :").Action(); use && err == nil {
		filterData = LineToDict(filterStr)
		oper = oper.Grep(filterData)
	}
	MemoryLastInfo(new_data_source, oper.Name, oper.Filter, oper.Page, oper.Len)
	after(bottle, oper)

}

func SearchByLastSearchInfo(after func(bottle BottleInterface, oper *Oper), errHandle func(error)) {
	ColorL("use last searcher", MemLastInfo.LastConnectStr)
	bottle, err := AutoBottle(ParsePath(MemLastInfo.LastConnectStr))
	if err != nil {
		errHandle(err)
		return
	}
	oper := OperWith(MemLastInfo.LastTbStr)
	oper.Filter = MemLastInfo.LastGrep
	oper.Page = MemLastInfo.MemPage
	oper.Len = MemLastInfo.MemPageLen
	after(bottle, oper)
}

func SearchFileByUI(fileName string, after func(bottle BottleInterface, oper *Oper), errHandle func(error)) {

	bottle, err := AutoBottle(ParsePath(fileName))
	if err != nil {
		errHandle(err)
		return
	}
	var oper *Oper
	if bottle.GetTp() == "mongodb" {
		if use, db, err := FlowFromList("> 输入数据库表名/Collection 名字:").Action(); use && err == nil {

			oper = OperWith(db)
		} else {
			errHandle(err)
			return
		}
	} else if strings.Contains(bottle.GetTp(), "sql") {
		tbs := bottle.Desc(OperWith(""))
		if len(tbs) > 1 {
			if use, tb, err := FlowFromList(tbs...).Action(); use && err == nil {
				oper = OperWith(tb)
			} else {
				errHandle(err)
				log.Println("[Ui create Error:]", err)
			}

		} else {
			oper = OperWith(tbs[0])
		}

	} else if bottle.GetTp() == "SqlFile" {
		tbs := bottle.Desc(OperWith(""))
		if len(tbs) > 1 {
			if use, tb, err := FlowFromList(tbs...).Action(); use && err == nil {
				oper = OperWith(tb)
			} else {
				errHandle(err)
				log.Println("[Ui create Error:]", err)
			}

		} else {
			oper = OperWith(tbs[0])
		}

	} else {
		oper = OperWith("")
	}
	var filterData G
	keys := bottle.Desc(oper)
	keysTip := strings.Join(keys, ",")
	if use, filterStr, err := FlowFromList("> 可以检索的关键字 \n" + keysTip + "  :").Action(); use && err == nil {
		filterData = LineToDict(filterStr)
		oper = oper.Grep(filterData)
	}
	MemoryLastInfo(fileName, oper.Name, oper.Filter, oper.Page, oper.Len)
	after(bottle, oper)
}

func ParsePath(sourcePath string) (f string) {
	usr, _ := user.Current()
	HOME := usr.HomeDir
	if strings.Contains(sourcePath, "~") {
		sourcePath = strings.ReplaceAll(sourcePath, "~", HOME)
	} else if strings.HasPrefix(sourcePath, "file://") {
		sourcePath = strings.Replace(sourcePath, "file://", "", 1)
	}

	if strings.HasSuffix(sourcePath, ".xlsx") {
		f = fmt.Sprintf("xlsx://%s", sourcePath)
	} else if strings.HasSuffix(sourcePath, ".db") {
		f = fmt.Sprintf("sqlite3://%s", sourcePath)
	} else if strings.HasSuffix(sourcePath, ".sql") {
		f = fmt.Sprintf("sql://%s", sourcePath)
	} else if strings.HasSuffix(sourcePath, ".csv") {
		f = fmt.Sprintf("text://%s", sourcePath)
	} else {
		rip := regexp.MustCompile(`(\d{1,3}\.){3}(\d{1,3})`)
		ip := rip.FindString(sourcePath)
		if sourcePath == "localhost" {
			ip = "127.0.0.1"
		}
		if ip != "" {
			var pre string
			var endtail string
			if strings.Contains(sourcePath, "://") {
				tmps := strings.SplitN(sourcePath, "://", 2)
				pre = tmps[0]
				endtail = tmps[1]
			} else {
				_, pre, _ = FlowFromList("mysql", "mongodb").Action()
			}

			if pre == "mongodb" {
				_, portS, _ := FlowFromList(">Port:27017").Action()
				f = fmt.Sprintf("mongodb://%s:%s", ip, portS)

			} else {
				var user string
				var pass string
				// var use bool
				_, user, _ = FlowFromList(">User:root").Action()
				_, pass, _ = FlowFromList(">Password *:").Action()
				if endtail != "" && strings.Contains(endtail, "/") {
					database := strings.Split(strings.Split(endtail, "/")[1], "?")[0]
					// _, database, _ := FlowFromList(">database name :").Action()
					f = fmt.Sprintf("mysql://%s:%s@tcp(%s)/%s?charset=utf8", user, pass, ip, database)
				} else {
					_, database, _ := FlowFromList(">database name :").Action()
					f = fmt.Sprintf("mysql://%s:%s@tcp(%s)/%s?charset=utf8", user, pass, ip, database)
				}

			}

		} else {
			f = sourcePath
		}
	}
	return
}
